﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ClassLibrary1
{
    public class Game
    {
        public const byte blackKing = 3;
        public const byte black = 4;
        public const byte redKing = 2;
        public const byte red = 1;
        public const byte blank = 0;

        const byte stillMyTurn = Byte.MaxValue;

        const bool redTurn = true;
        const bool blackTurn = false;

        Tuple<byte, byte> notSelected = Tuple.Create(Byte.MaxValue, Byte.MaxValue);

        private byte[,] board;
        private bool turn = redTurn;
        private bool forceSelect = false;
        private Tuple<byte, byte> selected;
        private byte numBlack = 12;
        private byte numRed = 12;

        private NetClient client;
        private NetServer server;

        bool isNetworked = true;

        public bool RedsTurn
        {
            get { return turn; }
            set { turn = value; }
        }

        public bool IsNetworked
        {
            get { return isNetworked; }
            set { isNetworked = value; }
        }

        public delegate void drawBoard(byte[,] d);

        static String server_ip = "192.168.1.37";
        private static bool isSever = false;

        public static String ServerIP
        {
            get { return server_ip; }
            set { server_ip = value; }
        }

        public static bool IsServer
        {
            get { return isSever; }
            set { isSever = value; }
        }

        public byte[,] getBoard()
        {
            return board;
        }

        public void ClickSquare(Tuple<byte, byte> location, out bool locselected, out bool done, drawBoard d, bool isRed)
        {
            byte numLeft = 0;

            selectSquare(location, ref numLeft, out locselected, isRed);

            if (numLeft != stillMyTurn)
            {
                if (numLeft == 0)
                {
                    Console.WriteLine("You WIN!");
                    done = true;
                }
                else
                    done = false;

                if (isNetworked)
                {
                    //Send board over
                    if (isSever)
                    {
                        server.SendBoard(board);
                        d(board);
                        new Thread(() => { board = server.GetBoard(); Console.WriteLine("Got Board"); d(board); turn = redTurn; }).Start();
                    }
                    else
                    {
                        client.SendBoard(board);
                        d(board);
                        new Thread(() => { board = client.GetBoard(); Console.WriteLine("Got Board"); d(board); turn = redTurn; }).Start();
                    }

                    turn = blackTurn;
                }
                return;
            }
            done = false;
        }


        public Game()
        {
            selected = notSelected;

            setupBoard();

        }

        public void connect(drawBoard d)
        {
            if (!isSever)
            {
                client = new NetClient(server_ip);
                new Thread(() => { board = client.GetBoard(); Console.WriteLine("Got Board"); d(board); turn = redTurn; }).Start();
            }
            if (isSever)
            {
                server = new NetServer(server_ip);
                new Thread(() => { board = server.GetBoard(); Console.WriteLine("Got Board"); d(board); turn = redTurn; }).Start();
            }
        }
        /**
         * Returns stillMyTurn (-1) if after click a space, it is still this computers turn (or still the opponents turn)
         * Otherwise, this computers turn is done and it returns the number of opponent pieces
         **/
        private void selectSquare(Tuple<byte, byte> location, ref byte numLeft, out bool locselected, bool isRed)
        {
            if (selected == notSelected)
                locselected = false;
            else
                locselected = true;

            if (isNetworked && (server == null && client == null))
            {
                Console.WriteLine("Not connected!");
                numLeft = stillMyTurn;
                return;
            }

            // TODO 
            //Not my turn
            if (turn != redTurn && isNetworked)
            {
                Console.WriteLine("NOT YOUR TURN");
                numLeft = stillMyTurn;
                return;
            }

            //Invalid square
            if (location.Item1 % 2 == location.Item2 % 2)
            {
                Console.WriteLine("invalid square");
                numLeft = stillMyTurn;
                return;
            }
            if (!forceSelect && selected != notSelected && canMove(selected, location, isRed))
            {
                board[location.Item1, location.Item2] = board[selected.Item1, selected.Item2];
                board[selected.Item1, selected.Item2] = blank;

                if (shouldKing(location, isRed))
                {
                    if (isRed)
                        board[location.Item1, location.Item2] = redKing;
                    else
                        board[location.Item1, location.Item2] = blackKing;
                }

                selected = notSelected;

                turn = !turn;

                Console.WriteLine("Moved");

                locselected = false;

                numLeft = numBlack;

                return;
            }
            else if (selected != notSelected && canJump(selected, location, isRed))
            {
                board[location.Item1, location.Item2] = board[selected.Item1, selected.Item2];
                board[selected.Item1, selected.Item2] = blank;

                Tuple<byte, byte> jumped = getJumpedSquare(selected, location);

                board[jumped.Item1, jumped.Item2] = blank;
                if (isRed)
                    numBlack--;
                else
                    numRed--;

                if (shouldKing(location, isRed))
                {
                    if (isRed)
                        board[location.Item1, location.Item2] = redKing;
                    else
                        board[location.Item1, location.Item2] = blackKing;
                }

                if (!canStillJump(location, isRed))
                {
                    turn = !turn;
                    forceSelect = false;
                    selected = notSelected;

                    locselected = false;

                    Console.WriteLine("jumped");

                    numLeft = numBlack;
                    return;
                }
                else
                {
                    Console.WriteLine("jumped but can jump again");

                    forceSelect = true;
                    selected = location;

                    numLeft = stillMyTurn;
                    return;
                }
            }
            else if (!forceSelect && (board[location.Item1, location.Item2] == red || board[location.Item1, location.Item2] == redKing))
            {
                Console.WriteLine("selected");
                Console.WriteLine("TURN: " + isRed);

                locselected = true;
                selected = location;
                numLeft = stillMyTurn;
                return;
            }
            else if (!forceSelect && (board[location.Item1, location.Item2] == black || board[location.Item1, location.Item2] == blackKing))
            {
                Console.WriteLine("selected");
                Console.WriteLine("TURN: " + isRed);
              
                locselected = true;
                selected = location;
                numLeft = stillMyTurn;
                return;
            }
            else
            {
                Console.WriteLine("nothing");

                numLeft = stillMyTurn;
                return;
            }
        }

        private bool shouldKing(Tuple<byte, byte> location, bool isRed)
        {
            if (isRed && board[location.Item1, location.Item2] == red && location.Item1 % 2 == 1 && location.Item2 == 0)
                return true;
            if (!isRed && board[location.Item1, location.Item2] == black && location.Item1 % 2 == 0 && location.Item2 == 7)
                return true;

            return false;
        }
        private bool canStillJump(Tuple<byte, byte> location, bool isRed)
        {
            byte baseType, baseKing;
            byte opType, opKing;
            if (isRed)
            {
                baseType = red;
                baseKing = redKing;
                opType = black;
                opKing = blackKing;
            }
            else
            {
                baseType = black;
                baseKing = blackKing;
                opType = red;
                opKing = redKing;
            }

            if (board[location.Item1, location.Item2] == baseKing && (location.Item1 < 6 && location.Item2 < 6 && (board[location.Item1 + 1, location.Item2 + 1] == opType || board[location.Item1 + 1, location.Item2 + 1] == opKing) && board[location.Item1 + 2, location.Item2 + 2] == blank))
                return true;
            else if (location.Item1 < 6 && location.Item2 > 1 && (board[location.Item1 + 1, location.Item2 - 1] == opType || board[location.Item1 + 1, location.Item2 - 1] == opKing) && board[location.Item1 + 2, location.Item2 - 2] == blank)
                return true;
            else if (location.Item1 > 1 && location.Item2 > 1 && (board[location.Item1 - 1, location.Item2 - 1] == opType || board[location.Item1 - 1, location.Item2 - 1] == opKing) && board[location.Item1 - 2, location.Item2 - 2] == blank)
                return true;
            else if (board[location.Item1, location.Item2] == baseKing && (location.Item1 > 1 && location.Item2 < 6 && (board[location.Item1 - 1, location.Item2 + 1] == opType || board[location.Item1 - 1, location.Item2 + 1] == opKing) && board[location.Item1 - 2, location.Item2 + 2] == blank))
                return true;
            else
                return false;
        }

        private bool canMove(Tuple<byte, byte> from, Tuple<byte, byte> to, bool isRed)
        {
            byte king, baseType;
            if (isRed)
            {
                baseType = red;
                king = redKing;
            }
            else
            {
                baseType = black;
                king = blackKing;
            }

            //Make sure they are not the same spot
            if (from.Item1 == to.Item1 || from.Item2 == to.Item2)
                return false;

            if (board[from.Item1, from.Item2] == baseType)
            {
                if (isRed && Math.Abs(to.Item1 - from.Item1) == 1 && to.Item2 - from.Item2 == -1)
                {
                    if (board[to.Item1, to.Item2] == blank)
                        return true;
                    else
                        return false;
                }
                else if (!isRed && Math.Abs(to.Item1 - from.Item1) == 1 && to.Item2 - from.Item2 == 1)
                {
                    if (board[to.Item1, to.Item2] == blank)
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
            else if (board[from.Item1, from.Item2] == king)
            {
                if (Math.Abs(to.Item1 - from.Item1) == 1 && Math.Abs(to.Item2 - from.Item2) == 1)
                    if (board[to.Item1, to.Item2] == blank)
                        return true;
                    else
                        return false;
                else
                    return false;
            }
            else
                return false;
        }

        private bool canJump(Tuple<byte, byte> from, Tuple<byte, byte> to, bool isRed)
        {
            byte baseType, baseKing, opType, opKing;
            if (isRed)
            {
                baseType = red;
                baseKing = redKing;
                opType = black;
                opKing = blackKing;
            }
            else
            {
                baseType = black;
                baseKing = blackKing;
                opType = red;
                opKing = redKing;
            }

            //Make sure they are not the same spot
            if (from.Item1 == to.Item1 || from.Item2 == to.Item2)
                return false;

            if (board[from.Item1, from.Item2] == baseType)
            {
                if ((isRed && Math.Abs(to.Item1 - from.Item1) == 2 && to.Item2 - from.Item2 == -2) || (!isRed && Math.Abs(to.Item1 - from.Item1) == 2 && to.Item2 - from.Item2 == 2))
                {

                    Tuple<byte, byte> mid = getJumpedSquare(from, to);

                    if ((board[mid.Item1, mid.Item2] == opType || board[mid.Item1, mid.Item2] == opKing) && board[to.Item1, to.Item2] == blank)
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
            else if (board[from.Item1, from.Item2] == baseKing)
            {
                if (Math.Abs(to.Item1 - from.Item1) == 2 && Math.Abs(to.Item2 - from.Item2) == 2)
                {
                    Tuple<byte, byte> mid = getJumpedSquare(from, to);

                    if ((board[mid.Item1, mid.Item2] == opType || board[mid.Item1, mid.Item2] == opKing) && board[to.Item1, to.Item2] == blank)
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
            else
                return false;
        }

        private Tuple<byte, byte> getJumpedSquare(Tuple<byte, byte> from, Tuple<byte, byte> to)
        {
            Tuple<byte, byte> mid;
            if (to.Item1 - from.Item1 == 2)
                if (to.Item2 - from.Item2 == 2)
                    mid = Tuple.Create((byte)(from.Item1 + 1), (byte)(from.Item2 + 1));
                else
                    mid = Tuple.Create((byte)(from.Item1 + 1), (byte)(from.Item2 - 1));
            else
                if (to.Item2 - from.Item2 == 2)
                    mid = Tuple.Create((byte)(from.Item1 - 1), (byte)(from.Item2 + 1));
                else
                    mid = Tuple.Create((byte)(from.Item1 - 1), (byte)(from.Item2 - 1));

            return mid;
        }

        private void setupBoard()
        {
            board = new byte[8, 8];

            board[0, 7] = red;
            board[2, 7] = red;
            board[4, 7] = red;
            board[6, 7] = red;

            board[1, 6] = red;
            board[3, 6] = red;
            board[5, 6] = red;
            board[7, 6] = red;

            board[0, 5] = red;
            board[2, 5] = red;
            board[4, 5] = red;
            board[6, 5] = red;

            board[1, 2] = black;
            board[3, 2] = black;
            board[5, 2] = black;
            board[7, 2] = black;

            board[0, 1] = black;
            board[2, 1] = black;
            board[4, 1] = black;
            board[6, 1] = black;

            board[1, 0] = black;
            board[3, 0] = black;
            board[5, 0] = black;
            board[7, 0] = black;
        }
    }

}
